Maybe Monad

Monad Pattern
C++은 태생적으로 객체지향 프로그래밍 언어이지만, 함수 객체(std::function)과 람다를 사용하면
제한적이지만, 함수형 프로그래밍 언어에서 사용되는 모나드(monad) 디자인 패턴을 구현할 수 있다.

(모나드 패턴은 함수형 프로그래밍 언어에서 훨씬 더 효과적으로 사용할 수 있기는 함)
~라면 모나드(Maybe Monad)
어떤 값이 존재하는지 여부를 알 수 있는 방법
- nullptr에 의미를 부여하여 존재하지 않음을 표시
- shared_ptr과 같은 스마트 포인터를 이용하여 존재 여부를 검사
- STL 라이브러리의 std::optional<T>를 이용해 값이 존재할 때는 타입 T의 값을 저장하고,
 존재하지 않을 때는 std::nullopt를 저장함
struct Address{
string* house_name=nullptr;
};
struct Person{
Address* address=nullptr;
};
// Drill Dwon
void print_house_name(Person* p){
if(p!=nullptr &&
p->address!=nullptr &&
p->address->house_name!=nullptr){
cout<<*p->address->house_name<<endl;
}
}
위와 같이 nullptr 여부를 검사하기 위해 내부 구조를 파고들어 가는 과정을 드릴-다운(Drill-Down)이라고
부른다.
위와 같은 침습적 과정을 “~라면 모나드”를 이용해 함수형으로 표현할 수 있다.
template <typename T>
struct Maybe{
T* context;
Maybe(T* context): context(context) {}
};
// 릿
template <typename T>
Maybe<T> maybe(T* context){
return Maybe<T>(context);
}
클래스의 생성자를 통해서 템플릿 파라미터의 클래스를 추론할 수 없기 때문에
위와 같이 전역 편의 함수를 이용해서 T를 추론해 낸다.

- context!=nullptr; 인 경우, 객체에 대한 드릴-다운을 진행
- context==nullptr; 인 경우, 아무것도 하지 않는다.
template <typename Func>
auto With(Func evaluator){
return context!=nullptr? maybe(evaluator(context)): nullptr;
}
template <tyepname TFunc>
auto Do(TFunc action){
if(context!=nullptr) action(context);
return *this;
}
관례적으로 함수에 context를 넘겨주기 전에 아무런 변경을 가하지 않는다.
//
void print_house_name(Person* p){
auto z=maybe(p)
.With([](auto x){ return x->address; })
.With([](auto x){ return x->house_name; })
.Do([](auto x){ cout<<*x<<endl; });
}
~라면 모나드를 잉ㅇ하여 흐름식 인터페이스(fluent interface)를 만든다.
함수 호출이 필요한 지점들이 사슬을 이루며 구성되어 있다.
(각 연산(With, Do)는 *this 혹은 new Maybe<T> 타입을 리턴한다.